
#include "common/basictypes.h"
#include "common/code32gcc.h"
#include "common/linkage.h"
#include "i386/kernel/screen.h"
#include "i386/kernel/segment.h"

asmlinkage void divide_error(void);
asmlinkage void debug(void);
asmlinkage void nmi(void);
asmlinkage void int3(void);
asmlinkage void overflow(void);
asmlinkage void bounds(void);
asmlinkage void invalid_op(void);
asmlinkage void device_not_available(void);
asmlinkage void coprocessor_segment_overrun(void);
asmlinkage void invalid_TSS(void);
asmlinkage void segment_not_present(void);
asmlinkage void stack_segment(void);
asmlinkage void general_protection(void);
asmlinkage void page_fault(void);
asmlinkage void coprocessor_error(void);
asmlinkage void simd_coprocessor_error(void);
asmlinkage void alignment_check(void);
asmlinkage void spurious_interrupt_bug(void);
asmlinkage void machine_check(void);

struct idt_desc {
  u16 low_offset;
  u16 seg_selector;
  u16 high_offset;
  u16 flags;
} __attribute__((__packed__));

struct idt_desc idt_table[256] __attribute__((section(".data.idt"))) = {{0, 0},};

/*
 * idt descriptor 的结构可以参考 intel manual volumn3, chapter6, figure 6-2
 */

#define _set_gate(gate_addr,type,dpl,addr,seg) \
  do { \
    int __d0, __d1; \
    __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
          "movw %4,%%dx\n\t" \
          "movl %%eax,%0\n\t" \
          "movl %%edx,%1" \
          :"=m" (*((long *) (gate_addr))), \
           "=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
          :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
           "3" ((char *) (addr)),"2" ((seg) << 16));   \
  } while (0)


/*
static void _set_gate(struct idt_desc* desc, u32 type, u32 dpl, u32 addr, u16 seg) {
  desc->low_offset = (u16)(addr & 0x0000ffff);
  desc->high_offset= (u16)((addr & 0xFFFF0000) >> 16);
  desc->seg_selector = seg;
  desc->flags = 0x8000 | (dpl << 12 ) | (type << 8);
}
*/


void __init set_intr_gate(u32 n, u32 addr) {
  _set_gate(idt_table + n, 14, 0, (u32)addr, __KERNEL_CS);
}

/*
 */
static inline __init void set_system_intr_gate(u32 n, u32 addr) {
  _set_gate(idt_table + n, 14, 3, addr, __KERNEL_CS);
}

static inline __init void set_trap_gate(u32 n, u32 addr) {
  _set_gate(idt_table + n, 14, 3, addr, __KERNEL_CS);
}

static inline __init void set_system_gate(u32 n, u32 addr) {
  _set_gate(idt_table + n, 14, 3, addr, __KERNEL_CS);
}

static inline __init void set_task_gate(u32 n, u32 gdt_entry) {
  _set_gate(idt_table + n, 5, 0, 0, (gdt_entry)<<3);
}


void __init trap_init(void) {
  /*
   *  init idt
   * 在文件的开始部分声明了一个256大小的数组，这个数组的每一项都
   * 代表一个中断处理程序。 下面的代码负责将这个大表(idt)使用对应的函数
   * 填充。
   */
  set_trap_gate(0, (u32)&divide_error);
  set_intr_gate(1, (u32)&debug);
  set_intr_gate(2, (u32)&nmi);
  set_system_intr_gate(3, (u32)&int3); /* int3-5 can be called from all */
  set_system_gate(4, (u32)&overflow);
  set_system_gate(5, (u32)&bounds);
  set_trap_gate(6, (u32)&invalid_op);
  set_trap_gate(7, (u32)&device_not_available);
  set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);
  set_trap_gate(9, (u32)&coprocessor_segment_overrun);
  set_trap_gate(10, (u32)&invalid_TSS);
  set_trap_gate(11, (u32)&segment_not_present);
  set_trap_gate(12, (u32)&stack_segment);
  set_trap_gate(13, (u32)&general_protection);
  set_intr_gate(14, (u32)&page_fault);
  set_trap_gate(15, (u32)&spurious_interrupt_bug);
  set_trap_gate(16, (u32)&coprocessor_error);
  set_trap_gate(17, (u32)&alignment_check);
#ifdef CONFIG_X86_MCE
  set_trap_gate(18, (u32)&machine_check);
#endif
  // set_trap_gate(19, (u32)&simd_coprocessor_error);

  /*
   * load idt
   */

  /*
    test ud2
  */
  /*
  __asm__ __volatile__(
      "ud2"
                       );
  */
}


asmlinkage void do_divide_error(void) {
  puts("#DE");
}

asmlinkage void do_debug(void) {
  puts("#DB");
}

asmlinkage void do_nmi(void) {
  puts("#NMI");
}

asmlinkage void do_int3(void) {
  puts("#BP");  
}

asmlinkage void do_overflow(void) {
  puts("#OF");
}

asmlinkage void do_bounds(void) {
  puts("#BR");
}

asmlinkage void do_invalid_op(void) {
  puts("#UD");
}

asmlinkage void do_device_not_available(void) {
  puts("#NM");
}

asmlinkage void do_coprocessor_segment_overrun(void) {
  puts("Exception: coprocessor_segment_overrun");
}

asmlinkage void do_invalid_TSS(void) {
  puts("#TS");
}

asmlinkage void do_segment_not_present(void) {
  puts("#NP");
}

asmlinkage void do_stack_segment(void) {
  puts("#SS");
}

asmlinkage void do_general_protection(void) {
  puts("#GP");
}

asmlinkage void do_page_fault(void) {
  puts("Exception: page fault");
}

asmlinkage void do_coprocessor_error(void) {
  puts("Exception: coprocessor error");
}

asmlinkage void do_simd_coprocessor_error(void) {
  puts("Exception: simd coprocessor error");
}

asmlinkage void do_alignment_check(void) {
  puts("Exception: alignment check");
}

asmlinkage void do_spurious_interrupt_bug(void) {
  puts("Exception: supurious interrupt bug");
}

asmlinkage void do_machine_check(void) {
  puts("#MC");
}
